home *** CD-ROM | disk | FTP | other *** search
- Path: news.wwa.com!rmartin
- From: rmartin@oma.com (Robert C. Martin)
- Newsgroups: comp.object,comp.lang.c++
- Subject: Re: A design question
- Date: 26 Feb 1996 15:17:22 GMT
- Organization: Object Mentor
- Message-ID: <RMARTIN.96Feb26091722@rcm.oma.com>
- References: <1996Feb22.234825.18755@dcs.warwick.ac.uk>
- <pvcybpuqx6p.fsf@hln56.pki-nbg.philips.de> <312FF1D9.7B02@crl.com>
- NNTP-Posting-Host: rcm.oma.com
- In-reply-to: Richard Berman's message of Sat, 24 Feb 1996 21:21:29 -0800
-
-
- > miro@dcs.warwick.ac.uk (Miroslav J Walker) writes:
- >
- > I'm trying to model input from a user being processed over a
- > series of steps. Input is parsed, put into a queue and then
- > interpreted on a timer. My question is how to model the idea
- > of it being the same bit of data (essentially) taking on
- > several forms as it proceeds along the 'pipeline' of
- > processing.
- >
-
- Harald Wellmann wrote:
- >
- > There's a book called "Design Patterns" by Erich Gamma, where the pattern
- > you seem to be thinking of is called a "Chain of Responsibility".
-
-
- Richard Berman <sts@crl.com> writes:
-
- Of course, a state table might do the trick as well.
-
-
- Indeed. And there is a very nice pattern in the "Design Patterns"
- book that describes the way in which a state machine can be
- implemented to do just what Miroslav is asking. The pattern is called
- "State".
-
- Consider the following:
-
- class MyParcel; /fwd declare.
-
- // this class is the abstract representation of the state of the
- // parcel of data. It defines functions which respond to all the
- // events that a parcel may experience.
-
- class ParcelState
- {
- public:
- // the events that occur along the processing chain.
- virtual void Event1(MyParcel&) = 0;
- virtual void Event2(MyParcel&) = 0;
- };
-
- // This is the parcel itself. It also defines functions which respond
- // to all its events. However, these functions simply delegate to the
- // contained state object.
-
- class MyParcel
- {
- public:
- MyParcel();
- void Event1() {itsState->Event1(*this);}
- void Event2() {itsState->Event2(*this);}
- private:
- ParcelState* itsState;
- };
-
-
- We can create derivatives of ParcelState for each state that a
- 'MyParcel' object can exist in. We can then define the implementation
- of the Event functions so that in each state the correct operations
- take place.
-
- Consider the following state table:
-
- // Starting State Event Ending State Action
- // -------------- ----- ------------ ------
- Raw Ready Parsed Parse
- Raw Cancel End Delete
-
- Parsed QTime Enqueued Enqueue
- Parsed Cancel End Delete
-
- Enqueued ExecTime Enqueued Execute
- Enqueued Pause Paused Dequeue
- Enqueued Cancel End Dequeue,Delete
-
- Paused Resume Enqueued Enqueue
- Paused Cancel End Delete
-
- This table describes the behavior of a parcel through a number of
- states and events. The parcel begins in the raw state. When it is
- ready to be parsed, the Parse operation is invoked, and the parcel
- goes to the Parsed state. When it it time to put the parcel on the
- queue, then the Enqueue operation is invoked and the parcel goes to
- the 'Enqueued' state.
-
- The state machine above handles cancelation of the parcel at any
- particular state. Note that in the 'Enqueued' state the 'Cancel'
- event invokes two actions (Dequeue,Delete) whereas in every other
- state is simply invokes one action (Delete).
-
- To encode this state machine using the 'State' pattern shown above can
- be a little tedious. For that reason I have written a compiler which
- takes state tables like the above and translates them into C++ code
- that uses the 'State' Pattern.
-
- Below is the output generated by the state map compiler for the above
- FSM. If anybody would like a copy of the source code for this
- compiler, drop me an email message and I'll mail it to you.
-
- ---------------------st.sm (actual input file) -----------------
- FSMName Parcel
- Context MyParcel
- Initial Raw
- Header parcel.h
- {
-
- // Starting State Event Ending State Action
- // -------------- ----- ------------ ------
- Raw Ready Parsed Parse
- Raw Cancel End Delete
-
- Parsed QTime Enqueued Enqueue
- Parsed Cancel End Delete
-
- Enqueued ExecTime Enqueued Execute
- Enqueued Pause Paused Dequeue
- Enqueued Cancel End {Dequeue Delete}
-
- Paused Resume Enqueued Enqueue
- Paused Cancel End Delete
-
- End * End {}
-
- }
-
- -----------------parcel.h (generated) ----------------------------
- #ifndef _H_Parcel
- #define _H_Parcel
- #include <stddef.h>
- #include "parcel.h"
- class Parcel;
-
- class ParcelState {
- public:
-
- virtual const char* StateName() const = 0;
- virtual void Resume(Parcel& s);
- virtual void Pause(Parcel& s);
- virtual void ExecTime(Parcel& s);
- virtual void QTime(Parcel& s);
- virtual void Cancel(Parcel& s);
- virtual void Ready(Parcel& s);
- };
-
- class ParcelEndState : public ParcelState {
- public:
- virtual const char* StateName() const
- {return("End");};
- };
-
- class ParcelPausedState : public ParcelState {
- public:
- virtual const char* StateName() const
- {return("Paused");};
- virtual void Cancel(Parcel&);
- virtual void Resume(Parcel&);
- };
-
- class ParcelEnqueuedState : public ParcelState {
- public:
- virtual const char* StateName() const
- {return("Enqueued");};
- virtual void Cancel(Parcel&);
- virtual void Pause(Parcel&);
- virtual void ExecTime(Parcel&);
- };
-
- class ParcelParsedState : public ParcelState {
- public:
- virtual const char* StateName() const
- {return("Parsed");};
- virtual void Cancel(Parcel&);
- virtual void QTime(Parcel&);
- };
-
- class ParcelRawState : public ParcelState {
- public:
- virtual const char* StateName() const
- {return("Raw");};
- virtual void Cancel(Parcel&);
- virtual void Ready(Parcel&);
- };
- class Parcel : public MyParcel {
- public:
- static ParcelPausedState PausedState;
- static ParcelEnqueuedState EnqueuedState;
- static ParcelEndState EndState;
- static ParcelParsedState ParsedState;
- static ParcelRawState RawState;
- Parcel();// default constructor
- void Resume() {itsState->Resume(*this);}
- void Pause() {itsState->Pause(*this);}
- void ExecTime() {itsState->ExecTime(*this);}
- void QTime() {itsState->QTime(*this);}
- void Cancel() {itsState->Cancel(*this);}
- void Ready() {itsState->Ready(*this);}
- void SetState(ParcelState& theState) {itsState=&theState;}
- ParcelState& GetState() const {return *itsState;};
- private:
- ParcelState* itsState;
- };
- #endif
-
- ---------------parcel.cc (generated)---------------------
- #include "parcel.h"
- static char _versID[] = "No Version.";
- ParcelPausedState Parcel::PausedState;
- ParcelEnqueuedState Parcel::EnqueuedState;
- ParcelEndState Parcel::EndState;
- ParcelParsedState Parcel::ParsedState;
- ParcelRawState Parcel::RawState;
- void ParcelState::Resume(Parcel& s)
- {s.FSMError("Resume", s.GetState().StateName());}
- void ParcelState::Pause(Parcel& s)
- {s.FSMError("Pause", s.GetState().StateName());}
- void ParcelState::ExecTime(Parcel& s)
- {s.FSMError("ExecTime", s.GetState().StateName());}
- void ParcelState::QTime(Parcel& s)
- {s.FSMError("QTime", s.GetState().StateName());}
- void ParcelState::Cancel(Parcel& s)
- {s.FSMError("Cancel", s.GetState().StateName());}
- void ParcelState::Ready(Parcel& s)
- {s.FSMError("Ready", s.GetState().StateName());}
- void ParcelPausedState::Cancel(Parcel& s) {
- s.SetState(Parcel::EndState);
- s.Delete();
- }
- void ParcelPausedState::Resume(Parcel& s) {
- s.SetState(Parcel::EnqueuedState);
- s.Enqueue();
- }
- void ParcelEnqueuedState::Cancel(Parcel& s) {
- s.SetState(Parcel::EndState);
- s.Dequeue();
- s.Delete();
- }
- void ParcelEnqueuedState::Pause(Parcel& s) {
- s.SetState(Parcel::PausedState);
- s.Dequeue();
- }
- void ParcelEnqueuedState::ExecTime(Parcel& s) {
- s.SetState(Parcel::EnqueuedState);
- s.Execute();
- }
- void ParcelParsedState::Cancel(Parcel& s) {
- s.SetState(Parcel::EndState);
- s.Delete();
- }
- void ParcelParsedState::QTime(Parcel& s) {
- s.SetState(Parcel::EnqueuedState);
- s.Enqueue();
- }
- void ParcelRawState::Cancel(Parcel& s) {
- s.SetState(Parcel::EndState);
- s.Delete();
- }
- void ParcelRawState::Ready(Parcel& s) {
- s.SetState(Parcel::ParsedState);
- s.Parse();
- }
- Parcel::Parcel() : itsState(&RawState) {}
-
-
-
-
- --
- Robert Martin | Design Consulting | Training courses offered:
- Object Mentor Assoc.| rmartin@oma.com | OOA/D, C++, Advanced OO
- 14619 N. Somerset Cr| Tel: (847) 918-1004 | Mgt. Overview of OOT
- Green Oaks IL 60048 | Fax: (847) 918-1023 | Development Contracts.
-
-